/*
 * Written by Anders Fongen
 * Norwegian Defence Research Establishment (FFI)
 * Email: anders@fongen.no
 */

package no.ffi.gismoidm.idp;

import no.ffi.gismoidm.utils.GismoIdmException;
import no.ffi.gismoidm.utils.ClientProxy;
import no.ffi.gismoidm.utils.IdentityStatement;
import javax.security.auth.x500.X500Principal;
import no.ffi.gismoidm.utils.CrossCertificateException;
import no.ffi.tpm.crypto.CryptoOperations;

/**
 * @author anf
 * Created on 16.jul.2009
 * 
 * A cache for cross domain identityStatement. When idm(b) receives an identityStatement for a 
 * subject signed by idm(a), it may return the identityStatement signed by idm(b). The
 * client will need an identityStatement for idm(b) signed by idm(a), since this will be
 * needed by the client (in domain a) to authenticate services in domain b.
 * 
 * This class holds a cache of valid identityStatements and fetches new identityStatements
 * to replace the expired ones in a an on-demand manner
 */
public class XCOIIdentityStatementCache {
    int noOfPeerIDM=0;
    CacheEntry[] cacheTable;
    int futureSlack; // minutes
    static XCOIIdentityStatementCache xdIdentityStatementCache=null;
    X500Principal myDN=null;
    CryptoOperations crOp;
    
    private XCOIIdentityStatementCache(CryptoOperations crOp) {
        this.crOp = crOp;
        futureSlack = Integer.parseInt(Config.getProperty("IDENTITYSTATEMENT_XD_FUTURE_SLACK", "10"));
        noOfPeerIDM = Integer.parseInt(Config.getProperty("NO_OF_PEER_IDM"));
        try {
            myDN = crOp.getSubjectPrincipal();
        } catch (Exception e) { e.printStackTrace(); }
        cacheTable = new CacheEntry[noOfPeerIDM];
        for (int i=1; i<=noOfPeerIDM; i++) {
            String name = "IDM_SERVICE_" + i;
            String serviceURL = Config.getProperty(name);
            name ="IDM_DNAME_" + i;
            String issuerDN = Config.getProperty(name);
            cacheTable[i-1] = new CacheEntry(serviceURL, issuerDN, null);
        }
    }

    // Threadsafe
    public static synchronized XCOIIdentityStatementCache getInstance(CryptoOperations crOp) {
        // Singleton pattern
        if (xdIdentityStatementCache==null) 
            xdIdentityStatementCache = new XCOIIdentityStatementCache(crOp);
        return xdIdentityStatementCache;
    }
    
    // Threadsafe
    private CacheEntry retrieveEntry(X500Principal issuerDN) throws GismoIdmException {
        for (int i=0; i<noOfPeerIDM; i++) {
            CacheEntry ce = cacheTable[i];
            if (ce.issuerDN.equals(issuerDN))
                return ce;
        }
        throw new GismoIdmException("No cache entry for " + issuerDN + ", check configuration");
    }
    
    public IdentityStatement xdIdentityStatementFrom(X500Principal issuerDN) throws GismoIdmException {
        CacheEntry ce = retrieveEntry(issuerDN);
        if (!ce.valid(futureSlack))        
            // IdentityStatement for this issuerDN is missing or expired.
            // Go and get a new one
            try {
                // Next operation is atomic and therefore threadsafe (with regard to "ce")
                ce.xdAss = ClientProxy.getIdentityStatement(myDN.toString(),ce.serviceURL,crOp) ;
                // We don't want clientside exception like "SubjectNotFoundException" to be
                // returned, but have them converted into exception that more clearly
                // indicates a cross COI certificate problem
            } catch (GismoIdmException e) {
                throw new CrossCertificateException(e.getMessage());
            }
        return ce.xdAss;
    }
    
    
    class CacheEntry {
        String serviceURL; 
        X500Principal issuerDN;
        IdentityStatement xdAss;
        public CacheEntry(String serviceURL, String issuerDN, IdentityStatement xdAss) {
            this.serviceURL = serviceURL;
            this.issuerDN = new X500Principal(issuerDN);
            this.xdAss = xdAss;
        }
        public boolean valid(int future) {
            return (xdAss != null && !xdAss.identityStatementExpired(future));
        }
    }
}
